/*
 *Copyright (c) 2024 Black Sesame Technologies
 *
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License.
 *You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *Unless required by applicable law or agreed to in writing, software
 *distributed under the License is distributed on an "AS IS" BASIS,
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *See the License for the specific language governing permissions and
 *limitations under the License.
*/
#include "ipc_hw_layer.h"
#include "ipc_hw_impl.h"

/* this is ipc hw_layer msgbx implementation. In this file, which provide
 * API for upper layer and compabitility to different hardware ipc mechanism.
 */
/********************* local variables ***************************/
libipc_hw_compat_ops_t g_hw_ctl_ops;
static err_msg_callback err_cb;
static recv_ntf recv_cb;
static recv_ntf recv_simple_cb;
void* g_addr;

/********************* extern global variables *******************/
// NOTE: you should update your option pointer at here
extern libipc_hw_compat_ops_t msg_hw_ops;

// ipc hw layer local function
int32_t ipc_hw_register_ops(const libipc_hw_compat_ops_t *ops)
{
	if (!ops)
		return -1;

	g_hw_ctl_ops = *ops;
	return 0;
}

// ipc hw layer api implementation
int32_t ipc_hw_layer_init(const uint8_t endid,
			  const ipc_init_params_t *ipc_param,
			  msgbx_hw_info_t *hw_info)
{
	// step1: register ops
	int32_t ret = -1;
	ret = ipc_hw_register_ops(&msg_hw_ops);
	if (ret < 0)
		return -1;

	// step3: init hardware
	ret = g_hw_ctl_ops.ipc_hw_init(endid, ipc_param);
	if (ret < 0)
		return -3;

	// step2: get info from hw
	ret = g_hw_ctl_ops.ipc_hw_get_info(endid, hw_info);
	if (ret < 0)
		return -2;

		// step5: state mgt enable
#ifdef IPC_STATE_MGT_ENABLE
	if (ipc_param->msgbx_end_mgt_flag != 0) {
		ret = g_hw_ctl_ops.ipc_hw_sts_mgt_enble(
			endid, ipc_param->msgbx_end_mgt_flag);
		if (ret < 0)
			return -4;
	}
#endif

	return 0;
}
int32_t ipc_hw_layer_get_info(const uint8_t endid, msgbx_hw_info_t *hw_info)
{
	return g_hw_ctl_ops.ipc_hw_get_info(endid, hw_info);
}

int32_t ipc_hw_layer_deinit(const uint8_t endid)
{
	return g_hw_ctl_ops.ipc_hw_deinit(endid);
}

int32_t ipc_hw_layer_start(void)
{
	// step1:
	return 0;
}

// as for message receiving function, we use notify-getting strategy. It means that we would send a notification
// when we receive interrupt from msgbx default filter or dedicated config filter. IPC_trans_layer receive this
// notification, then call get_message actively from related filter fifo until it goes empty.
int32_t ipc_hw_layer_recv_ntf_register(void *addr, recv_ntf recv_func)
{
	g_addr = addr;
	recv_cb = recv_func;
	return 0;
}

/**
 * @brief recv simple protocol msg callback
 * 
 * @param recv_func 
 * @return int32_t 
 */
int32_t ipc_hw_layer_recv_ntf_simple_proto_register(recv_ntf recv_func)
{
    recv_simple_cb = recv_func;
    return 0;
}

int32_t ipc_hw_layer_get_msg(const uint8_t endid, rw_msg_t *msg,
			     const uint8_t fid)
{
	return g_hw_ctl_ops.ipc_hw_get_msg(endid, msg, fid);
}

int32_t ipc_hw_layer_send_msg(const uint8_t endid, const rw_msg_t *msg)
{
	// remote end state check
	// self tx fifo state check
	if (msg->header.cid == msg->header.pid) {
		if (recv_cb != NULL && g_addr != NULL) {
			// printf("loop back\n");
			return ((recv_ntf)recv_cb)(g_addr, 0, msg);
		}
	}
	return g_hw_ctl_ops.ipc_hw_send_msg(endid, msg);
}
#include "../../../library/printf_inc.h"
int32_t ipc_hw_recv_msg_notify(const uint8_t endid, const uint8_t fid,
			       const rw_msg_t *msg)
{
    // hprintf(DEBUG, "rmr debug ipc_hw_recv_msg_notify recv_cb:0x%x g_addr:0x%x\n",
    //         recv_cb, g_addr);
	if (recv_cb != NULL && g_addr != NULL) {
		return ((recv_ntf)recv_cb)(g_addr, fid, msg);
	}
	return 0;
}

int32_t ipc_hw_err_msg_notify(const uint8_t endid, const uint8_t fid)
{
#ifdef IPC_STATE_MGT_ENABLE
	// get err msg and call function
	msgbx_err_msg_t err_msg;
	g_hw_ctl_ops.ipc_hw_get_err_msg(endid, fid, &err_msg);
	if (err_cb != NULL) {
		((err_msg_callback)err_cb)(g_addr, &err_msg);
	}
#endif
	return 0;
}

// filtering rule processing
int32_t ipc_hw_layer_flt_init(const uint8_t endid, const msgbx_flt_cfg_t *cfg)
{
	int32_t ret = 0;
#ifdef IPC_FLT_MGT_ENABLE
	// filter state management enable
	ret = g_hw_ctl_ops.ipc_hw_flt_mgt_enble(endid, cfg->flt_id,
						cfg->mbx_flt_mgt_flag);
#endif
	return ret;
}

int32_t ipc_hw_layer_flt_rule_set(const uint8_t endid, const uint8_t fid,
				  msgbx_flt_rule_cfg_t *rule)
{
	// set rule to filter
	return g_hw_ctl_ops.ipc_hw_set_flt_cfg(endid, fid, rule);
}

int32_t ipc_hw_layer_flt_get_info(const uint8_t endid, const uint8_t fid,
				  msgbx_flt_rule_cfg_t *info)
{
	return g_hw_ctl_ops.ipc_hw_get_flt_info(endid, fid, info);
}

int32_t ipc_hw_layer_flt_rule_clr(const uint8_t endid, const uint8_t fid)
{
	return g_hw_ctl_ops.ipc_hw_flt_mgt_disable(endid, fid);
}

int32_t ipc_hw_layer_err_msg_register(const uint8_t endid,
				      err_msg_callback err_func)
{
	err_cb = err_func;
	return 0;
}

int32_t ipc_hw_layer_err_hdl(const uint8_t endid, uint8_t type, uint8_t id,
			     uint32_t hdl)
{
	return g_hw_ctl_ops.ipc_hw_err_hdl(endid, type, id, hdl);
}

int32_t ipc_hw_layer_get_time(const uint8_t endid, uint64_t *timestamp)
{
	return g_hw_ctl_ops.ipc_hw_get_time(timestamp);
}